import { Injectable } from '@nestjs/common';
import {
  BizException,
  CryptoUtil,
  JwtService,
  RbacService,
  TokenBlacklistService,
} from '@nbase/nest-mods';
import {
  type ILoginResponse,
  type IUserProfile,
  type IMenuTree,
  USER_STATUS,
  MENU_TYPE,
  SYSTEM_ROLE,
} from '@nbase/shared';
import { UserService } from '../system/user/user.service';
import { LoginDto } from './dto/login.dto';
import { ChangePasswordDto } from './dto/change-password.dto';
import { UpdateProfileDto } from './dto/update-profile.dto';
import { db, sysUserRole, sysRole, sysRoleMenu, sysMenu } from '@nbase/db';
import { eq, and, inArray } from 'drizzle-orm';

@Injectable()
export class AuthService {
  constructor(
    private readonly jwtService: JwtService,
    private readonly userService: UserService,
    private readonly rbacService: RbacService,
    private readonly tokenBlacklistService: TokenBlacklistService
  ) {}

  /**
   * 用户登录
   */
  async login(loginDto: LoginDto): Promise<ILoginResponse> {
    // 1. 查找用户
    const user = await this.userService.findByUsername(loginDto.username);
    if (!user) {
      throw BizException.invalidPassword('用户名或密码错误');
    }

    // 2. 验证密码
    const isPasswordValid = await CryptoUtil.comparePassword(
      loginDto.password,
      user.password
    );
    if (!isPasswordValid) {
      throw BizException.invalidPassword('用户名或密码错误');
    }

    // 3. 检查用户状态
    if (user.status !== USER_STATUS.ACTIVE) {
      throw BizException.userDisabled();
    }

    // 4. 查询用户的角色和权限
    const { roles, permissions } = await this.rbacService.getUserRolesAndPermissions(user.id);

    // 5. 生成 Token
    const tokenResult = await this.jwtService.login({
      id: user.id,
      username: user.username,
      roles,
      permissions,
    });

    // 6. 返回登录结果（包含用户信息）
    return {
      ...tokenResult,
      user: {
        username: user.username,
        nickname: user.nickname,
        email: user.email,
        mobile: user.mobile,
        avatar: user.avatar,
        deptId: user.deptId,
        status: user.status,
        roles,
        permissions,
      },
    } as ILoginResponse;
  }

  /**
   * 获取当前用户完整信息
   */
  async getProfile(userId: string): Promise<IUserProfile> {
    const user = await this.userService.findById(userId);
    if (!user) {
      throw BizException.userNotFound();
    }

    const { roles, permissions } = await this.rbacService.getUserRolesAndPermissions(user.id);

    return {
      username: user.username,
      nickname: user.nickname,
      email: user.email,
      mobile: user.mobile,
      avatar: user.avatar,
      deptId: user.deptId,
      status: user.status,
      roles,
      permissions,
    } as IUserProfile;
  }

  /**
   * 修改密码
   */
  async changePassword(userId: string, dto: ChangePasswordDto): Promise<void> {
    // 1. 查找用户
    const user = await this.userService.findById(userId);
    if (!user) {
      throw BizException.userNotFound();
    }

    // 2. 验证旧密码
    const isOldPasswordValid = await CryptoUtil.comparePassword(
      dto.oldPassword,
      user.password
    );
    if (!isOldPasswordValid) {
      throw BizException.invalidPassword('旧密码不正确');
    }

    // 3. 检查新旧密码是否相同
    if (dto.oldPassword === dto.newPassword) {
      throw BizException.invalidParams('新密码不能与旧密码相同');
    }

    // 4. 加密新密码
    const hashedPassword = await CryptoUtil.hashPassword(dto.newPassword);

    // 5. 更新密码
    await this.userService.updateUser(userId, {
      password: hashedPassword,
    });
  }

  /**
   * 更新个人信息
   */
  async updateProfile(userId: string, dto: UpdateProfileDto): Promise<IUserProfile> {
    const user = await this.userService.findById(userId);
    if (!user) {
      throw BizException.userNotFound();
    }

    // 更新用户信息
    await this.userService.updateUser(userId, dto);

    // 返回更新后的信息
    return this.getProfile(userId);
  }

  /**
   * 用户登出
   * 将 Token 加入黑名单，使其失效
   *
   * @param token JWT Token
   */
  async logout(token: string): Promise<void> {
    // 从配置获取 Token 过期时间
    const expiresIn = this.jwtService.getExpiresInSeconds();

    // 将 Token 加入黑名单
    await this.tokenBlacklistService.addToBlacklist(token, expiresIn);
  }

  /**
   * 获取当前用户有权限的菜单树（用于动态路由）
   * 只返回菜单类型（type = 1），不包括按钮类型（type = 2）
   * 超级管理员（super_admin）可以访问所有菜单
   */
  async getUserMenus(userId: string): Promise<IMenuTree[]> {
    // 1. 查询用户的所有角色
    const userRoles = await db
      .select({
        roleId: sysUserRole.roleId,
        roleCode: sysRole.code,
      })
      .from(sysUserRole)
      .innerJoin(sysRole, eq(sysUserRole.roleId, sysRole.id))
      .where(
        and(
          eq(sysUserRole.userId, userId),
          eq(sysRole.isDeleted, false),
          eq(sysRole.status, 1) // 只查询启用的角色
        )
      );

    if (userRoles.length === 0) {
      return [];
    }

    // 2. 检查是否为超级管理员
    const isSuperAdmin = userRoles.some((r) => r.roleCode === SYSTEM_ROLE.SUPER_ADMIN);

    let menus: any[];

    if (isSuperAdmin) {
      // 超级管理员：返回所有可见的菜单（不进行权限过滤）
      menus = await db
        .select({
          id: sysMenu.id,
          name: sysMenu.name,
          permission: sysMenu.permission,
          type: sysMenu.type,
          sort: sysMenu.sort,
          parentId: sysMenu.parentId,
          path: sysMenu.path,
          icon: sysMenu.icon,
          component: sysMenu.component,
          visible: sysMenu.visible,
          keepAlive: sysMenu.keepAlive,
        })
        .from(sysMenu)
        .where(
          and(
            eq(sysMenu.isDeleted, false),
            eq(sysMenu.visible, true), // 只查询可见的菜单
            eq(sysMenu.type, MENU_TYPE.MENU) // 只查询菜单类型，不包括按钮
          )
        )
        .orderBy(sysMenu.sort);
    } else {
      // 普通用户：根据角色权限查询菜单
      const roleIds = userRoles.map((r) => r.roleId);

      menus = await db
        .select({
          id: sysMenu.id,
          name: sysMenu.name,
          permission: sysMenu.permission,
          type: sysMenu.type,
          sort: sysMenu.sort,
          parentId: sysMenu.parentId,
          path: sysMenu.path,
          icon: sysMenu.icon,
          component: sysMenu.component,
          visible: sysMenu.visible,
          keepAlive: sysMenu.keepAlive,
        })
        .from(sysRoleMenu)
        .innerJoin(sysMenu, eq(sysRoleMenu.menuId, sysMenu.id))
        .where(
          and(
            inArray(sysRoleMenu.roleId, roleIds),
            eq(sysMenu.isDeleted, false),
            eq(sysMenu.visible, true), // 只查询可见的菜单
            eq(sysMenu.type, MENU_TYPE.MENU) // 只查询菜单类型，不包括按钮
          )
        )
        .orderBy(sysMenu.sort);

      // 去重（同一个菜单可能被多个角色拥有）
      menus = Array.from(
        new Map(menus.map((menu) => [menu.id, menu])).values()
      );
    }

    // 3. 构建树形结构
    return this.buildMenuTree(menus, null);
  }

  /**
   * 构建菜单树形结构
   */
  private buildMenuTree(menus: any[], parentId: string | null): IMenuTree[] {
    return menus
      .filter((menu) => menu.parentId === parentId)
      .map((menu) => ({
        ...menu,
        children: this.buildMenuTree(menus, menu.id),
      }))
      .filter((menu) => menu.children && menu.children.length > 0 || menu.path); // 过滤掉没有子菜单且没有路径的节点
  }

}

